home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 August: Tool Chest / Dev.CD Aug 00 TC Disk 1.toast / pc / sample code / contributed / waste / waste 1.3 / source / wedrawing.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-06-23  |  26.8 KB  |  985 lines

  1. /*
  2.  *    WEDrawing.c
  3.  *
  4.  *    WASTE PROJECT
  5.  *  Drawing routines and other basic support functions
  6.  *
  7.  *  Copyright (c) 1993-1998 Marco Piovanelli
  8.  *    All Rights Reserved
  9.  *
  10.  *  C port by Dan Crevier
  11.  *
  12.  */
  13.  
  14. #include "WASTEIntf.h"
  15.  
  16. #if WASTE_OBJECTS
  17.  
  18. pascal OSErr WEGetSelectedObject(WEObjectDescHandle *hObjectDesc, WEHandle hWE)
  19. {
  20.     WEPtr pWE = *hWE;
  21.     WERunInfo runInfo;
  22.  
  23.     // assume current selection is not an embedded object
  24.     *hObjectDesc = nil;
  25.  
  26.     // check selection range
  27.     if (pWE->selEnd - pWE->selStart == 1)
  28.     {
  29.         // check run info
  30.         WEGetRunInfo(pWE->selStart, &runInfo, hWE);
  31.         if ((*hObjectDesc = runInfo.runAttrs.runStyle.tsObject) != nil)
  32.         {
  33.             return noErr;
  34.         }
  35.     }
  36.     return weObjectNotFoundErr;
  37. }
  38.  
  39. pascal SInt32 WEFindNextObject(SInt32 offset, WEObjectDescHandle *hObjectDesc, WEHandle hWE)
  40. {
  41.     WEPtr pWE = *hWE;
  42.     WEStyleTableEntry *pStyles = *pWE->hStyles;
  43.     WEObjectDescHandle obj;
  44.     WERunArrayEntry *pRun;
  45.  
  46.     *hObjectDesc = nil;
  47.  
  48.     // do nothing if offset is already at the end of the text
  49.     if (offset >= pWE->textLength - 1)
  50.     {
  51.         return kInvalidOffset;
  52.     }
  53.  
  54.     // get a pointer to the run array entry immediately following offset
  55.     pRun = *pWE->hRuns + WEOffsetToRun(offset + 1, hWE);
  56.  
  57.     // perform a linear scan of the run array looking for a run whose
  58.     // corresponding style table entry points to an embedded object;
  59.     // the search will stop anyway because the last run array entry has styleIndex = -1
  60.     while (pRun->styleIndex >= 0)
  61.     {
  62.         if ((obj = pStyles[pRun->styleIndex].info.runStyle.tsObject) != nil)
  63.         {
  64.             *hObjectDesc = obj;
  65.             return pRun->runStart;
  66.         }
  67.         pRun++;
  68.     }
  69.  
  70.     return kInvalidOffset;
  71.  
  72. }
  73.  
  74. #endif  // WASTE_OBJECTS
  75.  
  76. pascal void _WEContinuousStyleRange(SInt32 rangeStart, SInt32 rangeEnd, WEStyleMode *mode, WETextStyle *ts, WEHandle hWE)
  77. {
  78.     // find out which style attributes are continous over the specified text range
  79.     // on entry, *mode specifies which attributes are to be checked
  80.     // on exit, *mode specifies the continuous attributes, also copied to ts
  81.  
  82.     SInt32 runIndex;
  83.     WEStyleMode outMode;
  84.     WERunInfo runInfo;
  85.  
  86.     outMode = *mode;
  87.  
  88.     // get style info at the beginning of the specified range
  89.     runIndex = WEOffsetToRun(rangeStart, hWE);
  90.     _WEGetIndStyle(runIndex, &runInfo, hWE);
  91.  
  92.     // copy the specified fields to ts
  93.     _WECopyStyle(&runInfo.runAttrs.runStyle, ts, normal, (outMode & weDoAll) | weDoReplaceFace);
  94.  
  95.     // loop through style runs across the current selection range
  96.     // if we determine that all specified attributes are discontinuous, we exit prematurely
  97.     do
  98.     {
  99.         _WEGetIndStyle(runIndex, &runInfo, hWE);
  100.  
  101.         // determine which attributes have changed, if any
  102.         if (outMode & weDoFont)
  103.         {
  104.             if (runInfo.runAttrs.runStyle.tsFont != ts->tsFont)
  105.             {
  106.                 outMode &= (~weDoFont);
  107.             }
  108.         }
  109.         if (outMode & weDoFace)
  110.         {
  111.             if (runInfo.runAttrs.runStyle.tsFace != ts->tsFace)
  112.             {
  113.                 ts->tsFace &= runInfo.runAttrs.runStyle.tsFace;
  114.                 if (ts->tsFace == 0)
  115.                 {
  116.                     outMode &= (~weDoFace);
  117.                 }
  118.             }
  119.         }
  120.         if (outMode & weDoFaceMask)
  121.         {
  122.             ts->tsFlags |= runInfo.runAttrs.runStyle.tsFace;
  123.         }
  124.         if (outMode & weDoSize)
  125.         {
  126.             if (runInfo.runAttrs.runStyle.tsSize != ts->tsSize)
  127.             {
  128.                 outMode &= (~weDoSize);
  129.             }
  130.         }
  131.         if (outMode & weDoColor)
  132.         {
  133.             if (!_WEBlockCmp(&runInfo.runAttrs.runStyle.tsColor, &ts->tsColor, sizeof(RGBColor)))
  134.             {
  135.                 outMode &= (~weDoColor);
  136.             }
  137.         }
  138.  
  139.         runIndex++;
  140.     } while ((outMode != 0) && (runInfo.runEnd < rangeEnd));
  141.  
  142.     *mode = outMode;
  143. }
  144.  
  145. pascal void _WESynchNullStyle(WEHandle hWE)
  146. {
  147.     // This routine fills the nullStyle field of the WE record with valid information
  148.     // and makes sure that the null style font belongs to the keyboard script.
  149.  
  150.     WEPtr pWE = *hWE;    // assume WE record is already locked
  151.     SInt32 runIndex;
  152.     WERunInfo runInfo;
  153. #if !WASTE_NO_SYNCH
  154.     ScriptCode keyboardScript;
  155.     SInt16 fontID;
  156. #endif
  157.  
  158.     // find the run index of the style run preceding the insertion point
  159.     runIndex = WEOffsetToRun(pWE->selStart - 1, hWE);
  160.  
  161.     // if the nullStyle record is marked as invalid, fill it with the style attributes
  162.     // associated with the character preceding the insertion point, and mark it as valid
  163.     if (!BTST(pWE->flags, weFUseNullStyle))
  164.     {
  165.         _WEGetIndStyle(runIndex, &runInfo, hWE);
  166.         pWE->nullStyle = runInfo.runAttrs;
  167.         BSET(pWE->flags, weFUseNullStyle);
  168.     }
  169.  
  170. #if !WASTE_NO_SYNCH
  171.     // if only the Roman script is installed, we're finished
  172.     if (!BTST(pWE->flags, weFNonRoman))
  173.     {
  174.         return;
  175.     }
  176.  
  177.     // *** FONT / KEYBOARD SYNCHRONIZATION ***
  178.     // get the keyboard script
  179.     keyboardScript = GetScriptManagerVariable(smKeyScript);
  180.  
  181.     // find out what font will be used for the next character typed
  182.     fontID = pWE->nullStyle.runStyle.tsFont;
  183.  
  184.     // do nothing if the font script is the same as the keyboard script
  185.     if (FontToScript(fontID) == keyboardScript)
  186.     {
  187.         return;
  188.     }
  189.  
  190.     // scan style runs starting from the insertion point backwards,
  191.     // looking for the first font belonging to the keyboard script
  192.     do
  193.     {
  194.         _WEGetIndStyle(runIndex, &runInfo, hWE);
  195.         fontID = runInfo.runAttrs.runStyle.tsFont;
  196.         if (FontToScript(fontID) == keyboardScript)
  197.         {
  198.             break;
  199.         }
  200.         runIndex--;
  201.     } while (runIndex>=0);
  202.  
  203.     // if no font was ever used for the keyboard script, default to the
  204.     // application font for the script
  205.     if (runIndex < 0)
  206.     {
  207.         fontID = GetScriptVariable(keyboardScript, smScriptAppFond);
  208.     }
  209.  
  210.     // change the font in the null style record
  211.     pWE->nullStyle.runStyle.tsFont = fontID;
  212. #endif
  213. }
  214.  
  215. pascal Boolean WEContinuousStyle(WEStyleMode *mode, TextStyle *ts, WEHandle hWE)
  216. {
  217.     // find out which style attributes are continous over the selection range
  218.     // on entry, the mode bitmap specifies which attributes are to be checked
  219.     // on exit, the mode bitmap specifies the continuous attributes, also copied to ts
  220.     // return true if all specified attributes are continuous
  221.  
  222.     WEPtr pWE;
  223.     WEStyleMode oldMode;
  224.     Boolean continuousStyle;
  225.     Boolean saveWELock;
  226.  
  227.     // lock the WE record
  228.     pWE = *hWE;
  229.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  230.  
  231.     // mask out unused bits in *mode
  232.     *mode &= (weDoAll | weDoFaceMask);
  233.  
  234.     // two rather different paths are taken depending on whether
  235.     // the selection range is empty or not
  236.     if (pWE->selStart == pWE->selEnd)
  237.     {
  238.         // if the selection range is empty, always return true and set ts
  239.         // from the nullStyle record, after having validated it
  240.         continuousStyle = true;
  241.         _WESynchNullStyle(hWE);
  242.         _WECopyStyle(&pWE->nullStyle.runStyle, (WETextStyle *) ts, normal, (*mode & weDoAll) | weDoReplaceFace);
  243.     }
  244.     else
  245.     {
  246.         // otherwise get the continuous style attributes over the selection range
  247.         oldMode = *mode;
  248.         _WEContinuousStyleRange(pWE->selStart, pWE->selEnd, mode, (WETextStyle *) ts, hWE);
  249.  
  250.         // return true if mode hasn't changed
  251.         continuousStyle = (oldMode == *mode);
  252.     }
  253.  
  254.     // unlock the WE record
  255.     _WESetHandleLock((Handle) hWE, saveWELock);
  256.  
  257.     return continuousStyle;
  258. }
  259.  
  260. static pascal Boolean GetSegmentDirection(SInt16 segmentIndex, const struct FormatOrderData *data)
  261. {
  262.     return _WEGetIndDirection(data->firstRun + segmentIndex, data->hWE);
  263. }
  264.  
  265. pascal void _WESegmentLoop(SInt32 firstLine, SInt32 lastLine, WESegmentLoopProcPtr callback, void *callbackData, WEHandle hWE)
  266. {
  267.     // For each style segment on every line in the specified range, set up
  268.     // text attributes in the port and call the callback.
  269.     // the WE record must be already locked
  270.  
  271.     WEPtr pWE = *hWE;
  272.     WELineRec *pLine;
  273.     Ptr pText;
  274.     SInt32 lineIndex;
  275.     SInt32 firstRun;
  276.     SInt32 segmentIndex, segmentCount;
  277.     SInt32 runIndex, currentRunIndex;
  278.     SInt32 lineStart, lineEnd, segmentStart, segmentEnd;
  279.     JustStyleCode styleRunPosition;
  280.     WERunInfo runInfo;
  281.     SInt16 autoOrdering[ kAutoOrderingSize ];
  282.     Handle hTempOrdering = nil;
  283.     SInt16 *pOrdering = nil;
  284.     struct FormatOrderData formatOrderData;
  285.     Boolean isRightToLeft;
  286.     Boolean saveLineLock;
  287.     Boolean saveTextLock;
  288.     QDEnvironment saveEnvironment;
  289.  
  290.     static StyleRunDirectionUPP directionProc = nil;
  291.  
  292.     // create a routine descriptor for GetSegmentDirection, if we haven't already
  293.     if (directionProc == nil)
  294.     {
  295.         directionProc = NewStyleRunDirectionProc(&GetSegmentDirection);
  296.     }
  297.  
  298.     // is right-to-left the dominant line direction?
  299.     isRightToLeft = IsRightToLeft(pWE->direction);
  300.  
  301.     // save the Quickdraw environment
  302.     _WESaveQDEnvironment(pWE->port, BTST(pWE->flags, weFHasColorQD) ? true : false, &saveEnvironment);
  303.  
  304.     // make sure firstLine and lastLine are within the allowed range
  305.     lineIndex = pWE->nLines - 1;
  306.     firstLine = _WEPinInRange(firstLine, 0, lineIndex);
  307.     lastLine = _WEPinInRange(lastLine, 0, lineIndex);
  308.  
  309.     // lock the line array
  310.     saveLineLock = _WESetHandleLock((Handle) pWE->hLines, true);
  311.     pLine = *pWE->hLines + firstLine;
  312.  
  313.     // lock the text
  314.     saveTextLock = _WESetHandleLock(pWE->hText, true);
  315.     pText = *pWE->hText;
  316.  
  317.     currentRunIndex = -1;
  318.  
  319.     // loop thru the specified lines
  320.     for ( lineIndex = firstLine; lineIndex <= lastLine; lineIndex++ )
  321.     {
  322.         // get line start and line end
  323.         lineStart = pLine[0].lineStart;
  324.         lineEnd = pLine[1].lineStart;
  325.  
  326.         // calculate the index to the first style run on the line
  327.         firstRun = WEOffsetToRun(lineStart, hWE);
  328.  
  329.         // calculate the number of segments on this line
  330.         segmentCount = WEOffsetToRun(lineEnd - 1, hWE) - firstRun + 1;
  331.  
  332.         if (BTST(pWE->flags, weFBidirectional))
  333.         {
  334.             // SPECIAL PROCESSING FOR BIDIRECTIONAL SCRIPTS
  335.             // we need an array of segmentCount entries for GetFormatOrder
  336.             // in most cases, stack storage will be sufficient
  337.             if (segmentCount <= kAutoOrderingSize)
  338.             {
  339.                 pOrdering = autoOrdering;
  340.             }
  341.             else
  342.             {
  343.                 // in rare cases the ordering array must be allocated dynamically
  344.                 _WEForgetHandle(&hTempOrdering);
  345.                 if (_WEAllocate(segmentCount * sizeof(SInt16), kAllocTemp, &hTempOrdering) != noErr)
  346.                 {
  347.                     // couldn't allocate memory: emergency exit
  348.                     break;
  349.                 }
  350.                 HLock(hTempOrdering);
  351.                 pOrdering = (SInt16 *) *hTempOrdering;
  352.             }
  353.  
  354.             // obtain the correct display order for the segments
  355.             if (segmentCount > 1)
  356.             {
  357.                 formatOrderData.firstRun = firstRun;
  358.                 formatOrderData.hWE = hWE;
  359.                 GetFormatOrder((FormatOrderPtr) pOrdering, 0, segmentCount - 1, isRightToLeft, directionProc, (Ptr) &formatOrderData);
  360.             }
  361.             else
  362.             {
  363.                 pOrdering[0] = 0;
  364.             }
  365.         }
  366.  
  367.         // loop thru each segment on this line
  368.         for ( segmentIndex = 0; segmentIndex < segmentCount; segmentIndex++ )
  369.         {
  370.             if (BTST(pWE->flags, weFBidirectional))
  371.             {
  372.                 runIndex = pOrdering[ segmentIndex ];
  373.             }
  374.             else
  375.             {
  376.                 runIndex = segmentIndex;
  377.             }
  378.             runIndex += firstRun;
  379.  
  380.             if (currentRunIndex != runIndex)
  381.             {
  382.                 // get style run information for the current style run
  383.                 _WEGetIndStyle(runIndex, &runInfo, hWE);
  384.  
  385.                 // set new text attributes
  386.                 TextFont(runInfo.runAttrs.runStyle.tsFont);
  387.                 TextFace(runInfo.runAttrs.runStyle.tsFace);
  388.                 TextSize(runInfo.runAttrs.runStyle.tsSize);
  389.  
  390.                 // remember current run index
  391.                 currentRunIndex = runIndex;
  392.             }
  393.  
  394.             // determine segment boundaries
  395.             segmentStart = (runInfo.runStart <= lineStart) ? lineStart : runInfo.runStart;
  396.             segmentEnd = (runInfo.runEnd >= lineEnd) ? lineEnd : runInfo.runEnd;
  397.  
  398.             // determine the relative position of this segment on the line
  399.             styleRunPosition = 0;        // onlyStyleRun
  400.             if (segmentIndex < segmentCount - 1)
  401.             {
  402.                 styleRunPosition += 1;    // leftStyleRun or middleStyleRun
  403.             }
  404.             if (segmentIndex > 0)
  405.             {
  406.                 styleRunPosition += 2;    // rightStyleRun or middleStyleRun
  407.             }
  408.  
  409.             // do the callback
  410.             if (callback(pLine, &runInfo.runAttrs, pText + segmentStart, segmentStart, segmentEnd - segmentStart, styleRunPosition, hWE, callbackData))
  411.             {
  412.                 break;
  413.             }
  414.         };
  415.         pLine++;
  416.     }
  417.  
  418.     // unlock the text
  419.     _WESetHandleLock(pWE->hText, saveTextLock);
  420.  
  421.     // unlock the line array
  422.     _WESetHandleLock((Handle) pWE->hLines, saveLineLock);
  423.  
  424.     // restore the Quickdraw environment
  425.     _WERestoreQDEnvironment(&saveEnvironment);
  426.  
  427.     // dispose of any temporary storage
  428.     _WEForgetHandle(&hTempOrdering);
  429. }
  430.  
  431. pascal void _WEDrawTSMHilite(Rect *segmentRect, UInt8 tsFlags)
  432. {
  433.     SInt16 underlineHeight;
  434.     RGBColor background, foreground, saveForeground;
  435.     Boolean isColorPort;
  436.     Boolean usingTrueGray;
  437.  
  438.     isColorPort = (((CGrafPtr) qd.thePort)->portVersion < 0);
  439.     usingTrueGray = false;
  440.  
  441.     // by default, the pen pattern is solid
  442.     PenPat(&qd.black);
  443.  
  444.     // if we're drawing in color, set the foreground color
  445.     if (isColorPort)
  446.     {
  447.         // save foreground color
  448.         GetForeColor(&saveForeground);
  449.  
  450.         // by default, the foreground color is black
  451.         foreground.red = 0;
  452.         foreground.green = 0;
  453.         foreground.blue = 0;
  454.  
  455.         // if we're underlining raw (unconverted) text, see if a "true gray" is available
  456.         if (!BTST(tsFlags, tsTSMConverted))
  457.         {
  458.             GetBackColor(&background);
  459.             usingTrueGray = GetGray(GetGDevice(), &background, &foreground);
  460.         } // if raw text
  461.  
  462.         // set the foreground color
  463.         RGBForeColor(&foreground);
  464.     } // if color graf port
  465.  
  466.     // if we're underlining raw (unconverted) text and no true gray is available,
  467.     // simulate gray with a 50% pattern
  468.     if (!BTST(tsFlags, tsTSMConverted))
  469.     {
  470.         if (!usingTrueGray)
  471.         {
  472.             PenPat(&qd.gray);
  473.         }
  474.     }
  475.     // use a 2-pixel tall underline if text is "selected", else use a 1-pixel tall underline
  476.     underlineHeight = BTST(tsFlags, tsTSMSelected) ? 2 : 1;
  477.  
  478.     // segmentRect becomes the rectangle to paint
  479.     InsetRect(segmentRect, 1, 0);
  480.     segmentRect->top = segmentRect->bottom - underlineHeight;
  481.  
  482.     // draw the underline
  483.     PaintRect(segmentRect);
  484.  
  485.     // restore the foreground color
  486.     if (isColorPort)
  487.     {
  488.         RGBForeColor(&saveForeground);
  489.     }
  490. }
  491.  
  492. static Boolean SLDraw
  493.     (
  494.         WELineRec *pLine,
  495.         const WERunAttributes *pAttrs,
  496.         Ptr pSegment,
  497.         SInt32 segmentStart,
  498.         SInt32 segmentLength,
  499.         JustStyleCode styleRunPosition,
  500.         WEHandle hWE,
  501.         void *callbackData
  502.     )
  503. {
  504. #pragma unused(segmentStart)
  505.     struct SLDrawData *cd = (struct SLDrawData *) callbackData;
  506.     WEPtr pWE = *hWE;
  507.     Fixed slop;
  508.     Rect segmentRect;
  509.     RGBColor rgbTemp1, rgbTemp2;
  510.  
  511.     // is this the first segment on this line?
  512.     if (IS_LEFTMOST_RUN(styleRunPosition))
  513.     {
  514.         // calculate the line rectangle (the rectangle which completely encloses the current line)
  515.         cd->lineRect.left = pWE->destRect.left;
  516.         cd->lineRect.right = pWE->destRect.right;
  517.         cd->lineRect.top = pWE->destRect.top + pLine[0].lineOrigin;
  518.         cd->lineRect.bottom = pWE->destRect.top + pLine[1].lineOrigin;
  519.  
  520.         // calculate the visible portion of this rectangle
  521.         // we do this by intersecting the line rectangle with the view rectangle
  522.         cd->drawRect = (*pWE->viewRgn)->rgnBBox;
  523.         SectRect(&cd->lineRect, &cd->drawRect, &cd->drawRect);
  524.  
  525.         if (cd->usingOffscreen)
  526.         {
  527.             // calculate the boundary rectangle for the offscreen buffer
  528.             // this is simply drawRect converted to global coordinates
  529.             cd->bounds = cd->drawRect;
  530.             LocalToGlobal((Point *) &cd->bounds.top);
  531.             LocalToGlobal((Point *) &cd->bounds.bottom);
  532.  
  533.             // update the offscreen graphics world for the new bounds (this could fail)
  534.             cd->drawingOffscreen = false;
  535.             if (! (UpdateGWorld((GWorldPtr *) &pWE->offscreenPort, 0, &cd->bounds, nil, nil, 0) & gwFlagErr))
  536.             {
  537.                 // NOTE: when running on a 68000 machine with the original Quickdraw,
  538.                 // a GWorld is just an extended GrafPort, and GetGWorldPixMap actually
  539.                 // returns a handle to a _copy_ of the GrafPort portBits (a BitMap, not a PixMap).
  540.                 // An important side-effect of this is that when we call SetOrigin,
  541.                 // only the original portBits is offset, not the copy.
  542.                 // get the pixel map associated with the offscreen graphics world
  543.                 cd->offscreenPixels = GetGWorldPixMap((GWorldPtr) pWE->offscreenPort);
  544.  
  545.                 // lock it down
  546.                 if (LockPixels(cd->offscreenPixels))
  547.                 {
  548.                     // offscreen pixel buffer allocation was successful
  549.                     cd->drawingOffscreen = true;
  550.  
  551.                     // get background color of onscreen port
  552.                     GetBackColor(&rgbTemp1);
  553.  
  554.                     // switch graphics world
  555.                     SetGWorld((GWorldPtr) pWE->offscreenPort, nil);
  556.  
  557.                     // synchronize the coordinate system of the offscreen port with that of the screen port
  558.                     SetOrigin(cd->drawRect.left, cd->drawRect.top);
  559.  
  560.                     // synchronize the background color
  561.                     RGBBackColor(&rgbTemp1);
  562.  
  563.                     // reset the offscreen clip region
  564.                     ClipRect(&cd->drawRect);
  565.                 }
  566.             } // if pixel buffer allocation was successful
  567.         } // if usingOffscreen
  568.  
  569.         // if doErase is true, erase the drawable area before drawing text
  570.         if (cd->doErase)
  571.         {
  572.             CallWEEraseProc(&cd->drawRect, hWE, pWE->eraseHook);
  573.         }
  574.  
  575.         // position the pen
  576.         MoveTo(cd->lineRect.left + _WECalcPenIndent(pLine, pWE->alignment, pWE->direction), cd->lineRect.top + pLine->lineAscent);
  577.     } // if first segment on line
  578.  
  579.     // if drawingOffscreen, switch thePort to the offscreen port
  580.     // and synchronize text attributes
  581.     if (cd->drawingOffscreen)
  582.     {
  583.         SetPort(pWE->offscreenPort);
  584.         TextFont(pAttrs->runStyle.tsFont);
  585.         TextFace(pAttrs->runStyle.tsFace);
  586.         TextSize(pAttrs->runStyle.tsSize);
  587.     } // if drawingOffscreen
  588.  
  589.     // get horizontal coordinate of the pen before drawing the segment
  590.     GetPen((Point *) &segmentRect.top);
  591.  
  592.     // set the foreground color
  593.     if (cd->usingColor)
  594.     {
  595.         RGBForeColor(&pAttrs->runStyle.tsColor);
  596.     }
  597.  
  598. #if WASTE_OBJECTS
  599.     if (pAttrs->runStyle.tsObject != nil)
  600.     {
  601.         _WEDrawObject(pAttrs->runStyle.tsObject);
  602.     }
  603.     else
  604. #endif
  605.     {
  606.         slop = 0;
  607.  
  608.         // calculate the "slop" (extra space) for this text segment (justified text only)
  609.         if (pWE->alignment == weJustify)
  610.         {
  611.             // if this is the last segment on the line, strip trailing spaces
  612.             if (IS_RIGHTMOST_RUN(styleRunPosition))
  613.             {
  614.                 segmentLength = VisibleLength(pSegment, segmentLength);
  615.             }
  616.             // calculate how much extra space is to be applied to this text segment
  617.             slop = FixMul(PortionLine(pSegment, segmentLength, styleRunPosition,
  618.                     kOneToOneScaling, kOneToOneScaling), pLine->lineJustAmount);
  619.  
  620.         } // if (alignment == weJustify)
  621.  
  622.         // draw the segment
  623.         CallWEDrawTextProc(pSegment, segmentLength, slop, styleRunPosition, hWE, pWE->drawTextHook);
  624.     }
  625.  
  626.     // get horizontal coordinate of the pen after drawing the segment
  627.     GetPen((Point *) &segmentRect.bottom);
  628.     segmentRect.bottom = cd->lineRect.bottom;
  629.  
  630.     // if this segment is in the TSM area, underline it in the appropriate way
  631.     if (BTST(pAttrs->runStyle.tsFlags, tsTSMHilite))
  632.     {
  633.         _WEDrawTSMHilite(&segmentRect, pAttrs->runStyle.tsFlags);
  634.     }
  635.  
  636.     if (cd->drawingOffscreen)
  637.     {
  638.         if (IS_RIGHTMOST_RUN(styleRunPosition))
  639.         {
  640.             // after drawing offscreen the last segment,
  641.             // prepare to copy the offscreen buffer to video RAM
  642.  
  643.             // first set the graphics world to the screen port
  644.             SetGWorld((CGrafPtr) cd->screenPort, cd->screenDevice);
  645.  
  646.             // before calling CopyBits, set the foreground color to black
  647.             // and the background to white to avoid colorization
  648.             if (cd->usingColor)
  649.             {
  650.                 GetBackColor(&rgbTemp1);
  651.                 rgbTemp2.red = 0xFFFF;
  652.                 rgbTemp2.green = 0xFFFF;
  653.                 rgbTemp2.blue = 0xFFFF;
  654.                 RGBBackColor(&rgbTemp2);
  655.                 rgbTemp2.red = 0;
  656.                 rgbTemp2.green = 0;
  657.                 rgbTemp2.blue = 0;
  658.                 RGBForeColor(&rgbTemp2);
  659.             }
  660.  
  661.             // copy the offscreen image of the [visible portion of the] line to the screen
  662.             CopyBits(&pWE->offscreenPort->portBits, &cd->screenPort->portBits,
  663.                      &cd->drawRect, &cd->drawRect, srcCopy, nil);
  664.  
  665.             // restore the original background color in the onscreen port
  666.             if (cd->usingColor)
  667.             {
  668.                 RGBBackColor(&rgbTemp1);
  669.             }
  670.  
  671.             // restore the original offscreen coordinate system and unlock the pixel image
  672.             SetPort(pWE->offscreenPort);
  673.             SetOrigin(0, 0);
  674.             if (cd->usingColor)
  675.             {
  676.                 RGBForeColor(&rgbTemp2);
  677.             }
  678.             UnlockPixels(cd->offscreenPixels);
  679.  
  680.         } // if last segment
  681.  
  682.         // restore the screen port for _WESegmentLoop
  683.         SetPort(cd->screenPort);
  684.     } // if drawingOffscreen
  685.  
  686.     return false;    // keep looping
  687. }
  688.  
  689. pascal void _WEDrawLines (SInt32 firstLine, SInt32 lastLine, Boolean doErase, WEHandle hWE)
  690. {
  691.     // draw the specified range of lines
  692.     // we can safely assume that the WE record is already locked
  693.     // and the port is already set the pWE->port
  694.  
  695.     WEPtr pWE = *hWE;            // assume WE record is locked
  696.     struct SLDrawData cd;        // block of data we'll pass to SLDraw
  697.  
  698.     BLOCK_CLR(cd);
  699.     cd.doErase = doErase;
  700.     cd.usingColor = (BTST(pWE->flags, weFHasColorQD) && !BTST(pWE->features, weFInhibitColor)) ? true : false;
  701.  
  702.     // do nothing if our graphics port is not visible
  703.     if (EmptyRgn(pWE->port->visRgn))
  704.     {
  705.         return;
  706.     }
  707.  
  708.     // save graphics world
  709.     GetGWorld((GWorldPtr *) &cd.screenPort, &cd.screenDevice);
  710.  
  711.     // If doErase is true, we're drawing over old text, so we must erase each line
  712.     // before redrawing it.  But if the weFDrawOffscreen feature is enabled, we draw
  713.     // the entire line offscreen and  we copy the image right over the old line,
  714.     // without erasing it, thus achieving a very smooth drawing effect.
  715.  
  716.     if ((doErase) && BTST(pWE->features, weFDrawOffscreen))
  717.     {
  718.         // has an offscreen world already been allocated?
  719.         if (pWE->offscreenPort == nil)
  720.         {
  721.             // nope,  create one; its bounds are set initially to an arbitrary rectangle
  722.             SetRect(&cd.bounds, 0, 0, 1, 1);
  723.  
  724.             // if NewGWorld fails, it will set pWE->offscreenPort to nil
  725.             NewGWorld((GWorldPtr *) &pWE->offscreenPort, 0, &cd.bounds, nil, nil, pixPurge + noNewDevice + useTempMem);
  726.         }
  727.         cd.usingOffscreen = (pWE->offscreenPort != nil);
  728.     }
  729.  
  730.     _WESegmentLoop(firstLine, lastLine, SLDraw, &cd, hWE);
  731.  
  732.     // restore graphics world
  733.     SetGWorld((GWorldPtr) cd.screenPort, cd.screenDevice);
  734. }
  735.  
  736. pascal SInt16 _WECalcPenIndent(const WELineRec *pLine, WEAlignment alignment, WEDirection direction)
  737. {
  738.     SInt16 retval = 0;
  739.  
  740.     switch (alignment)
  741.     {
  742.         case weFlushLeft:
  743.         {
  744.             break;
  745.         }
  746.  
  747.         case weFlushRight:
  748.         {
  749.             retval = pLine->lineSlop;
  750.             break;
  751.         }
  752.  
  753.         case weCenter:
  754.         {
  755.             retval = pLine->lineSlop / 2;
  756.             break;
  757.         }
  758.  
  759.         case weJustify:
  760.         {
  761.             if (pLine->lineJustAmount > 0)
  762.             {
  763.                 break;
  764.             }
  765.             // deliberate fall through to default case for
  766.             // last line of justified paragraph
  767.         }
  768.  
  769.         default:
  770.         {
  771.             if (IsRightToLeft(direction))
  772.             {
  773.                 retval = pLine->lineSlop;
  774.             }
  775.             break;
  776.         }
  777.     }
  778.  
  779.     return retval;
  780. }
  781.  
  782. pascal void _WESaveQDEnvironment(GrafPtr port, Boolean saveColor, QDEnvironment *environment)
  783. {
  784.     GetPort(&environment->envPort);
  785.     SetPort(port);
  786.     GetPenState(&environment->envPen);
  787.     PenNormal();
  788.     environment->envStyle.tsFont = port->txFont;
  789.     environment->envStyle.tsFace = port->txFace;
  790.     environment->envStyle.tsFlags = saveColor;        // remember if color was saved
  791.     environment->envStyle.tsSize = port->txSize;
  792.     if (saveColor)
  793.     {
  794.         GetForeColor(&environment->envStyle.tsColor);
  795.     }
  796.     environment->envMode = port->txMode;
  797.     TextMode(srcOr);
  798. }
  799.  
  800. pascal void _WERestoreQDEnvironment(const QDEnvironment *environment)
  801. {
  802.     SetPenState(&environment->envPen);
  803.     TextFont(environment->envStyle.tsFont);
  804.     TextFace(environment->envStyle.tsFace);
  805.     TextSize(environment->envStyle.tsSize);
  806.     TextMode(environment->envMode);
  807.     if (environment->envStyle.tsFlags)
  808.     {
  809.         RGBForeColor(&environment->envStyle.tsColor);
  810.     }
  811.     SetPort(environment->envPort);
  812. }
  813.  
  814. pascal void _WEFillFontInfo (GrafPtr port, WERunAttributes *targetStyle)
  815. {
  816.     // given a WERunAttributes record, fill in the runHeight, runAscent fields etc.
  817.     FontInfo fInfo;
  818.     QDEnvironment saveEnvironment;
  819.  
  820.     _WESaveQDEnvironment(port, false, &saveEnvironment);
  821.  
  822.     // we don't want a zero font size; although Quickdraw accepts zero to mean
  823.     // the default font size, it can cause trouble to us when we do calculations
  824.     if (targetStyle->runStyle.tsSize == 0)
  825.     {
  826.         targetStyle->runStyle.tsSize = 12;
  827.     }
  828.  
  829.     // set the text attributes
  830.     TextFont(targetStyle->runStyle.tsFont);
  831.     TextSize(targetStyle->runStyle.tsSize);
  832.     TextFace(targetStyle->runStyle.tsFace);
  833.     GetFontInfo(&fInfo);
  834.     targetStyle->runHeight = fInfo.ascent + fInfo.descent + fInfo.leading;
  835.     targetStyle->runAscent = fInfo.ascent;
  836.  
  837.     _WERestoreQDEnvironment(&saveEnvironment);
  838. }
  839.  
  840. pascal void _WECopyStyle(const WETextStyle *sourceStyle, WETextStyle *targetStyle,
  841.                 Style offStyles, WEStyleMode mode)
  842. {
  843.     // Copy some or all of the attributes composing sourceStyle to targetStyle.
  844.     // The mode parameter determines which attributes are to be copied and how.
  845.     // If mode contains weDoToggleFace,  offStyles indicates which
  846.     // Quickdraw styles are to be turned off.
  847.  
  848.     // COPY FONT
  849.     if (mode & weDoFont)
  850.     {
  851.             targetStyle->tsFont = sourceStyle->tsFont;
  852. #if WASTE_RESOLVE_FONT_DESIGNATORS
  853.             if (targetStyle->tsFont == systemFont)
  854.             {
  855.                 targetStyle->tsFont = GetSysFont();
  856.             }
  857.             if (targetStyle->tsFont == applFont)
  858.             {
  859.                 targetStyle->tsFont = GetAppFont();
  860.             }
  861. #endif
  862.     }
  863.  
  864.     // COPY SIZE
  865.     if (mode & (weDoSize | weDoAddSize))
  866.     {
  867.         // copy size to a long variable to avoid integer overflows when doing additions
  868.         SInt32 longSize = sourceStyle->tsSize;
  869.  
  870.         // zero really means 12
  871.         if (longSize == 0)
  872.         {
  873.             longSize = 12;
  874.         }
  875.  
  876.         // if kModeAddSize is set, the source size is added to the target size,
  877.         // otherwise the source size replaces the target size outright
  878.         if (mode & weDoAddSize)
  879.         {
  880.             longSize += targetStyle->tsSize;
  881.         }
  882.         // range-check the resulting size
  883.         longSize = _WEPinInRange(longSize, kMinFontSize, kMaxFontSize);
  884.         targetStyle->tsSize = longSize;
  885.     } // if alter size
  886.  
  887.     // COPY FACE
  888.     if (mode & (weDoFace | weDoFaceMask))
  889.     {
  890.         Style sourceFace = sourceStyle->tsFace;
  891.         Style targetFace = targetStyle->tsFace;
  892.  
  893.         if (mode & weDoFaceMask)
  894.         {
  895.             // USE MASK
  896.             // if kModeFaceMask is set, copy the Quickdraw styles (tsFace field)
  897.             // using the tsFlags field as a mask specifying which bits in the tsFace
  898.             // field are to be copied.
  899.             Style sourceMask = sourceStyle->tsFlags;
  900.             targetFace = (sourceFace & sourceMask) | (targetFace & (~sourceMask));
  901.         }
  902.         else
  903.         {
  904.             // IGNORE MASK
  905.             // sourceFace replaces targetFace outright if one or both of these conditions hold:
  906.             // 1. sourceFace is zero (= empty set = plain text)
  907.             // 2. the kModeReplaceFace bit is set
  908.  
  909.             if ((sourceFace == normal) || (mode & weDoReplaceFace))
  910.             {
  911.                 targetFace = sourceFace;
  912.             }
  913.             else
  914.             {
  915.                 // Otherwise sourceFace is interpreted as a bitmap indicating
  916.                 // which styles are to be altered -- all other styles are left intact.
  917.                 // What exactly happens to the styles indicated in sourceFace
  918.                 // depends on whether the kModeToggleFace bit is set or clear.
  919.  
  920.                 // if kModeToggleFace is set, turn a style off if it's set in offStyles,
  921.                 // else turn it on
  922.                 if (mode & weDoToggleFace)
  923.                 {
  924.                     targetFace = (sourceFace ^ offStyles) | (targetFace & (~sourceFace));
  925.                 }
  926.                 else
  927.                 {
  928.                     // if kModeToggleFace is clear, turn on the styles specified in sourceStyle
  929.                     targetFace |= sourceFace;
  930.                 }
  931.             }
  932.         }
  933.  
  934.         // the condense and extend attributes are mutually exclusive: if one is set
  935.         // in sourceFace, remove it from targetFace
  936.         if (sourceFace & condense)
  937.         {
  938.             targetFace &= (~extend);
  939.         }
  940.         if (sourceFace & extend)
  941.         {
  942.             targetFace &= (~condense);
  943.         }
  944.  
  945.         targetStyle->tsFace = targetFace;
  946.     }
  947.  
  948.     // COPY COLOR
  949.     if (mode & weDoColor)
  950.     {
  951.         targetStyle->tsColor = sourceStyle->tsColor;
  952.     }
  953.  
  954. #if WASTE_OBJECTS
  955.     // if kModeObject is set, copy object descriptor
  956.     if (mode & weDoObject)
  957.     {
  958.         targetStyle->tsObject = sourceStyle->tsObject;
  959.     }
  960. #endif
  961.  
  962.     // always clear targetStyle->tsFlags by default
  963.     targetStyle->tsFlags = 0;
  964.  
  965.     // if kModeFlags is set, copy the tsFlags field
  966.     if (mode & weDoFlags)
  967.     {
  968.         targetStyle->tsFlags = sourceStyle->tsFlags;
  969.     }
  970. }
  971.  
  972. pascal Boolean _WEOffsetInRange(SInt32 offset, WEEdge edge, SInt32 rangeStart, SInt32 rangeEnd)
  973. {
  974.     // return true if the position specified by the pair < offset, edge >
  975.     // is within the specified range
  976.  
  977.     // if edge is kTrailingEdge, offset really refers to the preceding character
  978.     if (edge == kTrailingEdge)
  979.     {
  980.         offset--;
  981.     }
  982.     // return true iff offset is within the specified range
  983.     return ((offset >= rangeStart) && (offset < rangeEnd));
  984. }
  985.